package game;

import java.util.Random;

import players.Player;

public class HexGame {

  private final HexGameFrame hexGameFrame;
  public final HexBoard hexBoard ; 
  private Player currentPlayer ;
  private String validToken ;

  
  public HexGame( HexGameFrame hexGameFrame, HexBoard hexBoard ) {
    this.hexGameFrame = hexGameFrame;
    this.hexBoard = hexBoard ; 
    this.currentPlayer = hexBoard.getPlayer(1) ;
    this.validToken = tokenGenerator() ; // An initial valid token.
    // Refer to tokenGenerator( ) to learn more about tokens
  }


  public Player getCurrentPlayer() { return currentPlayer; }


  public HexBoard getHexBoardCopy(){
    // Returns a new instance of the HexBoard associated with the game.
    return this.hexBoard.clone() ;
  }
  
  
  public void makeFirstMove() {
    int size = this.hexBoard.getSize();
    int randomIndex1 = (int)(Math.random() * size);
    int randomIndex2 = Math.random() < 0.5 ? 0 : (size-1);
    
    // Disallow the extreme corners: (0, 0) and (size-1, size-1).
    if (randomIndex1  == randomIndex2 && 
        (randomIndex1 == 0 || randomIndex1 == (size-1))) {
      makeFirstMove();
      return;
    }
    
    // Select North/South or East/West.
    if (Math.random() < 0.5) {
      int temp = randomIndex1;
      randomIndex1 = randomIndex2;
      randomIndex2 = temp;
    }
    moveCheckIn(new Cell(randomIndex1, randomIndex2, this.currentPlayer), validToken);
  }


  public void moveCheckIn(Cell move, String token) {
    try {

      if (!token.equals(this.validToken)) 
        throw new InvalidMoveException("Invalid token.") ;
      
      if (move.isResignation( ) && 
          !hexGameFrame.areYouSure(currentPlayer.getName() + 
                                   ", are you sure you want to resign?\n" +
                                   "If not, click \"No\" and select a move.")) {
        // This is a hokey fix. The players will be switched after moveCheckIn.
        // So switch them in advance to avoid a switch.
        switchPlayers();
        return;
      }

      if (!move.isResignation( )) {

        int x = move.getX();
        int y = move.getY();

        if (x < 0 || y < 0 || x >= this.hexBoard.getSize() || y >= this.hexBoard.getSize())
          throw new InvalidMoveException("Out of range("+this.hexBoard.getSize()+"). x: "+x+" y: "+y) ;

        if (!this.hexBoard.isEmpty(move)) 
          throw new InvalidMoveException("Cell is not empty.") ;
      }

    } catch (InvalidMoveException e) {
      Cell cell = this.hexBoard.selectFirstEmptyCell();
      move = cell.newCellWithNewPlayer(move.getPlayer());
    }

    this.hexBoard.setMove(move); 
    this.hexGameFrame.displayResistances();
  }


  public void proceed() {
    // Send an alert to the next player. Called when the "next move" button is pressed.
    currentPlayer.itsYourTurn( this, getHexBoardCopy(), tokenGenerator() ) ;
  }


  // Default visibility. Callable only from within this package.
  void proceedManually(Cell selectedCell) {
    moveCheckIn(selectedCell.newCellWithNewPlayer(currentPlayer), validToken);
  }


  public void resign() { proceedManually(resignMove()); }

  
  public Cell resignMove() { return new Cell(-1, -1, currentPlayer); }

  
  public void switchPlayers() {
    currentPlayer = hexBoard.getOtherPlayer(currentPlayer);
  }


  private String tokenGenerator() {
    // Tokens are unique string that are sent to a player when its turn comes.
    // Player should send back the token along with the answer. The game only accept the answer
    // with the latest generated token. In this case the other player cannot 
    // fool the system by playing in place of its rival, because it doesn't know the expected token.

    Random r = new Random();
    String token = Long.toString(Math.abs(r.nextLong()), 36);
    this.validToken = new String(token);
    return token;
  }


  ////////////////////////////////////////////////////

  public class InvalidMoveException extends Exception {

    private static final long serialVersionUID = 1L;

    InvalidMoveException(String msg) { super(msg); }
  }


}
